
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>CSV数据编辑器</title>
  <style>
    .el-table .el-table__cell .el-input__inner {
      overflow: visible;
      white-space: normal;
    }
    .el-table .el-table__cell {
      overflow: visible;
    }
  </style>
  <link rel="stylesheet" href="static/css/index.css">
  <script type="text/javascript" src="static/js/vue.js"></script>
  <script type="text/javascript" src="static/js/vue-resource.min.js"></script>
  <script type="text/javascript" src="static/js/vue-router.min.js"></script>
  <script type="text/javascript" src="static/js/element-ui.js"></script>
  <script type="text/javascript" src="static/js/axios.min.js"></script>
  <script type="text/javascript" src="static/js/mock.js"></script>
  <!-- Papa Parse for CSV handling -->
  <script src="static/js/papaparse.min.js"></script>
</head>
<body class="bg-gray-100">
<div id="app" class="container mx-auto p-4 w-full">
  <!-- 主容器 -->
  <div class="bg-white rounded-lg shadow-md p-6 mb-6">
    <!-- 按钮区域：占屏幕20% -->
    <div class="flex items-center justify-center mb-4" style="height: 20vh;">
      <el-card style="display: flex; width: 100%;">
        <div style="display: flex; width: 100%;">
          <el-upload
                  class="upload-demo el-upload--drag flex-1"
                  action="#"
                  :auto-upload="false"
                  :on-change="handleFileUpload"
                  :show-file-list="false"
                  accept=".csv"
                  drag
          >
            <div class="flex flex-col items-center justify-center p-2 h-full">
              <i class="el-icon-upload"></i>
              <div class="el-upload__text">将文件拖到此处，或<em>点击上传</em></div>
            </div>
          </el-upload>
          <div style="display: flex; gap: 10px; align-items: center; padding: 0 10px;">
            <el-input v-model="searchValue" placeholder="查找值"></el-input>
            <el-input v-model="replaceValue" placeholder="替换值"></el-input>
            <el-button type="primary" @click="batchReplace" size="medium">替换全部</el-button>
            <el-button type="danger" @click="resetData" icon="el-icon-refresh" size="medium">重置</el-button>
            <el-button type="primary" @click="downloadCSV" icon="el-icon-download" size="medium">下载</el-button>
          </div>
        </div>
      </el-card>
    </div>

    <!-- 数据表格区域：占屏幕80% -->
    <div v-if="csvData.length" style="height: 80vh;">
      <el-card style="height: 100%; display: flex; flex-direction: column;">
        <el-table
                v-loading="loading"
                :data="csvData"
                style="width: 100%;"
                height="100%"
                max-height="76vh"
                border
                fit
                highlight-current-row
        >
          <!-- 动态生成表格列 -->
          <el-table-column
                  v-for="(col, index) in headers || 0"
                  :key="index"
                  :prop="index.toString()"
                  :label="`列 ${index + 1}`"
                  show-overflow-tooltip
          >
            <template slot-scope="{ row, column }">
              <el-input
                      v-model="row[column.property]"
                      size="small"
                      @change="updateCell(row._index, column.property, $event)"
                      :title="row[column.property]"
              />
            </template>
          </el-table-column>
        </el-table>
        <div v-if="csvData.length > 0 && !headers.length" class="mt-3 text-sm text-yellow-500">
          警告: 数据存在但无法确定列数，请检查CSV格式
        </div>
      </el-card>
    </div>
  </div>
</div>

<script>
  // 创建Vue实例
  const app = new Vue({
    el: '#app',
    // 组件数据
    data: {
      csvData: [], // 存储解析后的CSV数据
      originalData: [], // 存储原始CSV数据，用于重置功能
      loading: false, // 加载状态指示器
      headers: [], // 存储表格列信息
      searchValue: '', // 查找值
      replaceValue: '' // 替换值
    },
    // 生命周期钩子：组件挂载后
    mounted() {
      console.log('应用已挂载到#app');
    },
    // 方法
    methods: {
      /**
       * 处理文件上传
       * @param {File} file - 上传的文件对象
       */
      handleFileUpload(file) {
        if (!file) return;

        this.loading = true;
        console.log('开始解析文件:', file.name);

        // 使用PapaParse解析CSV文件
        Papa.parse(file.raw, {
          header: false, // 不将第一行视为表头
          skipEmptyLines: true, // 跳过空行
          encoding: 'utf-8', // 使用UTF-8编码
          complete: (results) => {
            console.log('解析完成，结果:', results);

            // 检查解析结果是否为空
            if (!results.data || results.data.length === 0) {
              this.loading = false;
              this.$message.warning('CSV文件为空或格式不正确');
              return;
            }

            // 存储原始数据
            this.originalData = JSON.parse(JSON.stringify(results.data));
            // 为每行数据添加索引属性并转换为对象格式
            this.csvData = results.data.map((row, index) => {
              // 转换数组为对象，以便Element UI表格可以正确处理
              const rowObj = {};
              row.forEach((value, colIndex) => {
                rowObj[colIndex.toString()] = value || ''; // 确保值不为undefined
              });
              rowObj._index = index;
              return rowObj;
            });

            // 设置表头信息
            this.headers = results.data.length > 0 ? Array.from({length: results.data[0].length}, (_, i) => i) : [];
            console.log('表格列数:', this.headers.length);
            console.log('csvData内容:', this.csvData);

            this.loading = false;
            this.$message.success(`文件解析成功，共 ${this.csvData.length} 行数据，${this.headers.length} 列`);
            // 强制更新视图
            this.$forceUpdate();
          },
          error: (error) => {
            console.error('解析CSV文件出错:', error);
            this.$message.error('解析CSV文件出错: ' + error.message);
            this.loading = false;
          }
        });
      },

      /**
       * 更新表格单元格数据
       * @param {number} rowIndex - 行索引
       * @param {string} colIndex - 列索引（字符串形式）
       * @param {string} value - 新的单元格值
       */
      updateCell(rowIndex, colIndex, value) {
        // 确保索引有效
        if (rowIndex >= 0 && rowIndex < this.csvData.length) {
          this.csvData[rowIndex][colIndex] = value;
        }
      },

      /**
       * 下载修改后的CSV文件
       */
      downloadCSV() {
        // 准备下载的数据，移除索引属性并转换回数组格式
        const dataToExport = this.csvData.map(row => {
          const { _index, ...rest } = row;
          // 转换对象回数组格式并按列索引排序
          const rowArray = [];
          Object.keys(rest).sort((a, b) => parseInt(a) - parseInt(b)).forEach(key => {
            rowArray.push(rest[key]);
          });
          return rowArray;
        });

        // 配置Papa.unparse不包含表头
        const csv = Papa.unparse(dataToExport, { header: false });
        // 添加BOM以确保Windows系统能正确识别UTF-8编码
        const bom = new Uint8Array([0xEF, 0xBB, 0xBF]);
        const blob = new Blob([bom, csv], { type: 'text/csv;charset=utf-8;' });
        const url = URL.createObjectURL(blob);
        const link = document.createElement('a');
        link.href = url;
        link.setAttribute('download', 'modified_data.csv');
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        URL.revokeObjectURL(url); // 释放URL对象

        this.$message.success('CSV文件下载成功');
      },

      /**
       * 重置数据为原始状态
       */
      resetData() {
        // 重置数据并转换为对象格式
        this.csvData = JSON.parse(JSON.stringify(this.originalData)).map((row, index) => {
          // 转换数组为对象
          const rowObj = {};
          row.forEach((value, colIndex) => {
            rowObj[colIndex.toString()] = value;
          });
          rowObj._index = index;
          return rowObj;
        });
        this.$message.info('数据已重置');
      },

      /**
       * 批量替换表格中的值
       */
      batchReplace() {
        if (!this.searchValue.trim()) {
          this.$message.warning('请输入查找值');
          return;
        }

        if (!this.csvData.length) {
          this.$message.warning('没有数据可替换');
          return;
        }

        let replaceCount = 0;

        // 遍历所有行和列进行替换
        this.csvData.forEach(row => {
          // 获取所有列属性（排除_index）
          const columns = Object.keys(row).filter(key => key !== '_index');

          columns.forEach(col => {
            if (row[col] === this.searchValue) {
              row[col] = this.replaceValue;
              replaceCount++;
            }
          });
        });

        this.$message.success(`成功替换 ${replaceCount} 处匹配值`);
      }
    }
  });
</script>
</body>
</html>
